﻿//////////////////////////////////////////////////////////////////
//
// Network\Program.cs
//
// Copyright (c) 2012 Dan Pike.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using Interfaces;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Utilities;
using Factory;

namespace Network
{
   /// <summary>
   /// This is not the same as the other Program classes. This one starts everything up!
   /// </summary>
   public static class Program
   {
      ////////////////////////////////////////////////////////////////////////
      //
      public static Form MainForm;
      public static Logger Logger;
      public static Loader Loader;
      public static Configuration AppConfig;
      public static Config.Network Settings;
      public static IAlmanac Almanac;
      public static ITransport Transport;

      //////////////////////////////////////////////////////////////////////////
      //
      private static T loadConfiguration<T>(string sectionName) where T : ConfigurationSection, new()
      {
         try
         {
            AppConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            T settings = AppConfig.GetSection(sectionName) as T;
            if (null == settings)
            {
               settings = new T();
               AppConfig.Sections.Add(sectionName, settings);
               AppConfig.Save(ConfigurationSaveMode.Full);
            }
            return settings;
         }
         catch (System.Exception ex)
         {
            Logger.Exception(ex);
         }
         return null;
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Start up the system. This method is called from the Program.Main
      /// in the base application. We centralize this code so that everything
      /// gets initialized in the correct order (non-trivial!)
      /// </summary>
      public static bool Initialize<T>(Form mainForm, string configSection, out T programSettings)
         where T : ConfigurationSection, new()
      {
         Logger logger_ = null;
         programSettings = null;
         try
         {
            // Always start with the debug logger :)
            logger_ = Logger = new Logger();
            MainForm = mainForm;

            // Load the program's settings from the exe.config ...
            Logger.DebugFormat("loading configuration");
            if (null == (programSettings = loadConfiguration<T>(configSection)))
            {
               logger_.ErrorFormat("failed to load configuration file");
               return false;
            }
            // ... and link to the Network config section for our own stuff, too
            if (null == (Settings = AppConfig.GetSection(Config.Network.CONFIG_SECTION) as Config.Network))
            {
               logger_.ErrorFormat("configuration file does not have section '{0}'",
                  Config.Network.CONFIG_SECTION);
               return false;
            }
            
            Logger.DebugFormat("creating class factory");
            Factory.Program.Initialize(mainForm, Logger, AppConfig);
            Loader = new Factory.Loader();
            if (!Loader.LoadPlugins())
            {
               MessageBox.Show(string.Concat(Application.ProductName, " failed to initialize class factory"),
                  Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
               return false;
            }

            // Initialize the TypeConverters, so that the system will know how to read the configuration
            // files and create objects from the string representations that they hold
            Interfaces.Program.Loader = Loader;

            // Build the almanac from the configuration, if this application uses it
            if (Loader.CanCreate<Interfaces.IAlmanac>())
            {
               Logger.DebugFormat("creating almanac");
               Almanac = Loader.Create<Interfaces.IAlmanac>();
            }

            // Do the same for the Transport layer
            if (Loader.CanCreate<Interfaces.ITransport>())
            {
               Logger.DebugFormat("creating transport");
               Transport = Loader.Create<Interfaces.ITransport>();
            }

            // Initialize all of the plugins with the global data
            if (!Loader.InitializePrograms(MainForm, Almanac, Transport))
            {
               MessageBox.Show(string.Concat(Application.ProductName, " failed to initialize plugins"),
                  Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
               return false;
            }

            // Now everyone has access to the logger and config etc, we can initialize the scripts
            // that are managed by the Almanac
            if ((null != Almanac) && !Almanac.Initialize())
            {
               MessageBox.Show(string.Concat(Application.ProductName, " failed to initialize almanac"),
                  Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
               return false;
            }
            return true;
         }
         catch (System.Exception ex)
         {
            logger_.Exception(ex);
         }
         return false;
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Start up the system. This method is called from the Program.Main
      /// in the base application
      /// </summary>
      public static void shutdown()
      {
         try
         {
            Logger.DebugFormat("saving configuration changes");
            AppConfig.Save(ConfigurationSaveMode.Full);

            // Copy it back up to prevent losing it when VS2010 overwrites it
            if (AppConfig.FilePath.EndsWith(".vshost.exe.Config", StringComparison.CurrentCultureIgnoreCase))
            {
               string sourceFile = Path.Combine(
                  Path.GetDirectoryName(AppConfig.FilePath),
                  "..", "..", Application.ProductName,
                  "app.config"
                  );
               if (File.Exists(sourceFile) && (0 == (File.GetAttributes(sourceFile) & FileAttributes.ReadOnly)))
               {
                  File.Copy(AppConfig.FilePath, sourceFile, true);
               }
            }
         }
         finally
         {
         }
      }

      ////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Start up the system. This method is called from the Program.Main
      /// in the base application
      /// </summary>
      public static bool Initialize(Logger hostLogger, Configuration hostConfig,
         Loader hostLoader, IAlmanac hostAlmanac, ITransport transport)
      {
         try
         {
            Logger = hostLogger;
            AppConfig = hostConfig;
            Loader = hostLoader;
            Almanac = hostAlmanac;
            Transport = transport;

            string sectionName = Config.Network.CONFIG_SECTION;
            if (null == (Settings = AppConfig.GetSection(sectionName) as Config.Network))
            {
               Logger.ErrorFormat("could not find section '{0}' in configuration file '{1}'",
                  sectionName, AppConfig.FilePath);
               return false;
            }
            return true;
         }
         catch (System.Exception ex)
         {
            hostLogger.Exception(ex);
         }
         return false;
      }
   }
}
